﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using NewAI.FSM;
using NewAI.FSM.TankFSM;
using NewAI.Game;
using NewAI.Tools;

namespace NewAI
{
    public partial class Form1 : Form
    {
        private FSMManager fsm;
        private string configPath;

        private string[] startArgs;

        public Form1(string[] startArgs)
        {
            this.startArgs = startArgs;
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            configPath = Application.StartupPath;
            configPath += "\\Config.ini";
            ReadWeightData();

            MyInit();
        }

        private void MyInit()
        {
            if (startArgs != null && startArgs.Length > 0)
            {
                if (startArgs.Length >= 1)
                    textBox3.Text = startArgs[0];

                if (startArgs.Length >= 2)
                    textBox1.Text = startArgs[1];

                if (startArgs.Length >= 3)
                    textBox2.Text = startArgs[2];
            }


            bool checkPermission = true;

#if DEBUG
            checkPermission = false;
            checkBox3.Checked = true;//调试信息
            checkBox4.Checked = true;//发送Action状态
            checkBox5.Checked = true;//风险度图新
            checkBox6.Checked = true;//收益度图像
            checkBox7.Checked = true;//比率图像
            tabControl3.SelectedIndex = 1;
#endif

            if (checkPermission && CheckAdministratorPermission.IsAdmin())
            {
                MessageBox.Show(ErrorMsg.Permission);
                Application.Exit();
            }

            BeginStartInit();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            BeginStartInit();
        }

        Thread initThread;
        private void BeginStartInit()
        {
            if (initThread != null && initThread.IsAlive)
                initThread.Abort();
            button1.Text = "尝试连接游戏服务器...";
            button1.Enabled = false;
            initThread = new Thread(new ThreadStart(InitThread));
            initThread.Start();
        }

        GameMapDrawer drawer;
        private void InitThread()
        {
            fsm = FSMManager.GetInstance();
            PubliceMessage.DebugMessage.OnEvent += OnDebugText;
            PubliceMessage.SocketException.OnEvent += OnSocketException;
            PubliceMessage.ActionBulid.OnEvent += OnActionBulid;
            PubliceMessage.ActionTimeOut.OnEvent += OnActionTimeOut;

            fsm.InitFSM(
                textBox1.Text,
                Convert.ToInt32(textBox2.Text),
                new TeamInfo(Convert.ToInt32(textBox3.Text), textBox4.Text),
                Convert.ToInt32(textBox5.Text)
                );

            drawer = new GameMapDrawer(Convert.ToInt32(textBox3.Text), OnDrawerCallBack, Convert.ToInt32(textBox5.Text), 5);
            Invoke(new Action(() =>
            {
                button1.Text = "连接服务器成功！";
            }));
        }


        private void OnSocketException(object sender, FSMEventEventArg e)
        {
            button1.Invoke(new Action(() =>
            {
                button1.Text = "重新连接服务器";
                button1.Enabled = true;
            }));
        }

        Graphics g;
        private void OnDrawerCallBack(Bitmap bitmap)
        {
            Invoke(new Action(() =>
            {
                if (g == null)
                    g = pictureBox1.CreateGraphics();
                if (checkBox2.Checked)
                    g.DrawImage(bitmap, new Point(0, 0));
            }));
        }

        private void OnDebugText(object sender, FSMEventEventArg args)
        {
            if (args.Msg == "LegStart")
            {
                drawer.LegStart(args.Obj as LegStartData);
            }

            Invoke(new Action(() =>
            {
                if (checkBox5.Checked && args.Msg == "ThreatMatrix")
                {
                    DrawThreatImage(args.Obj as float[,]);
                }
                if (checkBox6.Checked && args.Msg == "ProfitMatrix")
                {
                    DrawProfitImage(args.Obj as float[,]);
                }
                if (checkBox3.Checked)
                {
                    if (textBox6.Text.Length > 2000)
                        textBox6.Text = textBox6.Text.Substring(textBox6.Text.Length - 2000);
                    textBox6.Text = textBox6.Text + "\r\n" + args.Msg;
                    textBox6.Select(textBox6.Text.Length, 0);
                    textBox6.ScrollToCaret();
                }
                if (checkBox1.Checked)
                {
                    ShowOurTanksInfo();
                }
                if (checkBox2.Checked)
                {
                    if (args.Msg == "Round")//TODO:需要回传Round信息
                        drawer.Round(args.Obj as RoundData);
                }
            }));
        }


        private Graphics threatGraphics;
        private void DrawThreatImage(float[,] matrix)
        {
            Invoke(new Action(() =>
            {
                if (threatGraphics == null)
                    threatGraphics = pictureBox2.CreateGraphics();

                Bitmap bitmap = MatrixToBitmap(matrix);
                threatGraphics.DrawImage(bitmap, new PointF(0, 0));
            }));
        }

        private Graphics profitGraphics;
        private void DrawProfitImage(float[,] matrix)
        {
            Invoke(new Action(() =>
            {
                if (profitGraphics == null)
                    profitGraphics = pictureBox3.CreateGraphics();


                Bitmap bitmap = MatrixToBitmap(matrix, new MatrixBitmapInfo() { ForegroundColor = Color.Green });
                profitGraphics.DrawImage(bitmap, new PointF(0, 0));
            }));
        }



        public class MatrixBitmapInfo
        {
            public int GridSize { set; get; } = 32;
            public float Min { set; get; } = 0;
            public float Max { set; get; } = 100;
            public bool DrawNumber { set; get; } = true;
            public int FontSize { set; get; } = 8;
            public string FontName { set; get; } = "微软雅黑";
            public Color BackgroundColor { set; get; } = Color.Black;
            public Color ForegroundColor { set; get; } = Color.Red;
        }
        public Bitmap MatrixToBitmap(float[,] matrix, MatrixBitmapInfo info = null)
        {
            if (info == null)
                info = new MatrixBitmapInfo();

            Bitmap bitmap = new Bitmap(matrix.GetLength(0) * info.GridSize, matrix.GetLength(1) * info.GridSize);
            Graphics g = Graphics.FromImage(bitmap);
            SolidBrush brush = new SolidBrush(Color.Red);
            SolidBrush fontBrush = new SolidBrush(Color.Yellow);

            g.Clear(Color.Black);
            int m = matrix.GetLength(0);
            int n = matrix.GetLength(1);
            float foregroundColorRage = 0;
            for (int i = 0; i < m; i++)
            {
                foregroundColorRage = 0;
                for (int j = 0; j < n; j++)
                {
                    if (matrix[i, j] <= info.Min)
                        foregroundColorRage = 0;
                    else if (matrix[i, j] >= info.Max)
                        foregroundColorRage = 1;
                    else
                        foregroundColorRage = matrix[i, j] / (info.Max - info.Min);
                    brush.Color = Color.FromArgb(
                        (int)(info.ForegroundColor.R * foregroundColorRage + info.BackgroundColor.R * (1 - foregroundColorRage)),
                        (int)(info.ForegroundColor.G * foregroundColorRage + info.BackgroundColor.G * (1 - foregroundColorRage)),
                        (int)(info.ForegroundColor.B * foregroundColorRage + info.BackgroundColor.B * (1 - foregroundColorRage))
                        );
                    g.FillRectangles(brush, new RectangleF[] { new RectangleF(j * info.GridSize, i * info.GridSize, info.GridSize, info.GridSize) });
                }
            }

            if (info.DrawNumber == false)
                return bitmap;

            Font font = new Font(info.FontName, info.FontSize);
            SizeF sizeF = g.MeasureString(info.Max.ToString(), font);//像素宽度
            int fontOffsetX = (int)((info.GridSize - sizeF.Width) / 2);
            int fontOffsetY = (int)((info.GridSize - sizeF.Height) / 2);
            for (int i = 0; i < m; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    g.DrawString(Math.Round(matrix[i, j], 1).ToString(), font, fontBrush, new PointF(j * info.GridSize, i * info.GridSize));
                }
            }

            return bitmap;
        }

        private int actionBulidCount = 0;
        private int actionTimeOutCount = 0;
        private void OnActionBulid(object sender, FSMEventEventArg args)
        {
            actionBulidCount++;
            Invoke(new Action(() =>
            {
                if (checkBox4.Checked)
                {
                    label26.Text = actionBulidCount.ToString();
                }
            }));
        }
        private void OnActionTimeOut(object sender, FSMEventEventArg args)
        {
            actionTimeOutCount++;
            Invoke(new Action(() =>
            {
                if (checkBox4.Checked)
                {
                    label27.Text = actionTimeOutCount.ToString();
                }
            }));
        }


        TankInfoBox[] infoBox;
        private void ShowOurTanksInfo()
        {
            if (infoBox == null)
                infoBox = new TankInfoBox[4] { tankInfoBox1, tankInfoBox2, tankInfoBox3, tankInfoBox4 };
            for (int i = 0; i < 4; i++)
            {
                infoBox[i].TankID = fsm.TankAIFSMs[i].TankID;
                infoBox[i].IsSurvival = fsm.TankAIFSMs[i].IsSurvival;
                if (fsm.TankAIFSMs[i].IsSurvival)
                {
                    infoBox[i].ThreatDegree = fsm.TankAIFSMs[i].ThreatDegree;
                    infoBox[i].ProfitDegree = fsm.TankAIFSMs[i].ProfitDegree;
                    infoBox[i].Position = fsm.TankAIFSMs[i].Position;
                    infoBox[i].FightMethod = fsm.TankAIFSMs[i].FightMethod;
                }
                else
                {
                    infoBox[i].ThreatDegree = -1;
                    infoBox[i].ProfitDegree = -1;
                    infoBox[i].FightMethod = "---";
                    infoBox[i].Position = new Point(-1, -1);
                }
            }
        }


        private void SaveWeightData()
        {
            try
            {
                File.WriteAllText(configPath, "");
            }
            catch (Exception)
            {
                MessageBox.Show(ErrorMsg.CreateIniFail);
                Application.Exit();
            }

            bool isOK = false;
            Invoke(new Action(() =>
            {
                isOK = IniReadWrite.Write(configPath, "威胁度权值", "敌人坦克距离威胁度权值", trackBar1.Value.ToString());
                isOK = IniReadWrite.Write(configPath, "威胁度权值", "子弹威胁度权值", trackBar2.Value.ToString());
                isOK = IniReadWrite.Write(configPath, "威胁度权值", "一定范围内敌方坦克数量威胁度权值", trackBar3.Value.ToString());
                isOK = IniReadWrite.Write(configPath, "威胁度权值", "一定范围内的友军数量权值", trackBar6.Value.ToString());
                isOK = IniReadWrite.Write(configPath, "威胁度权值", "一定范围内友军的平均距离权值", trackBar5.Value.ToString());
                isOK = IniReadWrite.Write(configPath, "威胁度权值", "剩余复活次数权值", trackBar4.Value.ToString());

                isOK = IniReadWrite.Write(configPath, "收益度权值", "附近金币量个数收益度权值", trackBar7.Value.ToString());
                isOK = IniReadWrite.Write(configPath, "收益度权值", "附近金币价值收益度权值", trackBar8.Value.ToString());
                isOK = IniReadWrite.Write(configPath, "收益度权值", "能否一回合干掉坦克收益度权值", trackBar9.Value.ToString());
            }));
            if (isOK == false)
            {
                MessageBox.Show(ErrorMsg.WriteIniFail);
                Application.Exit();
            }
        }

        private void ReadWeightData()
        {
            if (File.Exists(configPath) == false)
                return;
            try
            {
                string data;
                data = IniReadWrite.Read(configPath, "威胁度权值", "敌人坦克距离威胁度权值", TankAI.EnemyTanksDistanceThreatDegreeWeight.ToString());
                trackBar1.Value = Convert.ToInt32(data);

                data = IniReadWrite.Read(configPath, "威胁度权值", "子弹威胁度权值", TankAI.BulletDistanceThreatDegreeWeight.ToString());
                trackBar2.Value = Convert.ToInt32(data);

                data = IniReadWrite.Read(configPath, "威胁度权值", "一定范围内敌方坦克数量威胁度权值", TankAI.EnemyTanksCountThreatDegreeWeight.ToString());
                trackBar3.Value = Convert.ToInt32(data);

                data = IniReadWrite.Read(configPath, "威胁度权值", "一定范围内的友军数量权值", TankAI.OurTanksCountThreatDegreeWeight.ToString());
                trackBar6.Value = Convert.ToInt32(data);

                data = IniReadWrite.Read(configPath, "威胁度权值", "一定范围内友军的平均距离权值", TankAI.OurTanksDistanceThreatDegreeWeight.ToString());
                trackBar5.Value = Convert.ToInt32(data);

                data = IniReadWrite.Read(configPath, "威胁度权值", "剩余复活次数权值", TankAI.RemainLifeThreatDegreeWeight.ToString());
                trackBar4.Value = Convert.ToInt32(data);


                data = IniReadWrite.Read(configPath, "收益度权值", "附近金币量个数收益度权值", TankAI.AroundCoinsProfitDegreeWeight.ToString());
                trackBar7.Value = Convert.ToInt32(data);

                data = IniReadWrite.Read(configPath, "收益度权值", "附近金币价值收益度权值", TankAI.AroundCoinsValueProfitDegreeWeight.ToString());
                trackBar8.Value = Convert.ToInt32(data);

                data = IniReadWrite.Read(configPath, "收益度权值", "能否一回合干掉坦克收益度权值", TankAI.KillTanksProfitDegreeWeight.ToString());
                trackBar9.Value = Convert.ToInt32(data);

                SetWeights();
                ShowAllWeightValue();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"读取权值数据异常！\r\n异常信息：{ex.Message}");
                Application.Exit();
            }
        }

        private void ShowAllWeightValue()
        {
            label16.Text = $"{100 * trackBar1.Value / trackBar1.Maximum}";
            label17.Text = $"{100 * trackBar2.Value / trackBar2.Maximum}";
            label19.Text = $"{100 * trackBar3.Value / trackBar3.Maximum}";
            label18.Text = $"{100 * trackBar6.Value / trackBar6.Maximum}";
            label21.Text = $"{100 * trackBar5.Value / trackBar5.Maximum}";
            label20.Text = $"{100 * trackBar4.Value / trackBar4.Maximum}";

            label2.Text = $"{100 * trackBar7.Value / trackBar7.Maximum}";
            label7.Text = $"{100 * trackBar8.Value / trackBar8.Maximum}";
            label9.Text = $"{100 * trackBar9.Value / trackBar9.Maximum}";
        }

        private void SetWeights()
        {
            //敌人坦克距离权值
            TankAI.EnemyTanksDistanceThreatDegreeWeight = trackBar1.Value;

            //子弹威胁度权值
            TankAI.BulletDistanceThreatDegreeWeight = trackBar2.Value;

            //一定范围内敌方坦克数量权值
            TankAI.EnemyTanksCountThreatDegreeWeight = trackBar3.Value;

            //一定范围内的友军数量权值
            TankAI.OurTanksCountThreatDegreeWeight = trackBar6.Value;

            //一定范围内友军的平均距离权值
            TankAI.OurTanksDistanceThreatDegreeWeight = trackBar5.Value;

            //剩余复活次数权值
            TankAI.RemainLifeThreatDegreeWeight = trackBar4.Value;



            //附近金币量个数收益度权值
            TankAI.AroundCoinsProfitDegreeWeight = trackBar7.Value;

            //附近金币价值收益度权值
            TankAI.AroundCoinsValueProfitDegreeWeight = trackBar8.Value;

            //能否一回合干掉坦克收益度权值
            TankAI.KillTanksProfitDegreeWeight = trackBar9.Value;
        }

        private string[] processNames = { "cmd", "conhost", "tank_AI", "tank", "Server" };
        private void button2_Click(object sender, EventArgs e)
        {
            int count = 0;
            foreach (var item in Process.GetProcesses())
            {
                if (processNames.Contains(item.ProcessName))
                {
                    try
                    {
                        item.Kill();
                        count++;
                    }
                    catch (Exception)
                    {

                    }
                }
            }
            MessageBox.Show($"共关闭{count}个进程");
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            SaveWeightData();
            timer1.Stop();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {

        }

        public void CloseForm(IAsyncResult async)
        {
            notAllowClose = false;
            Application.Exit();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (initThread != null && initThread.IsAlive)
                initThread.Abort();
            if (fsm != null)
                fsm.Close(null);
            button1.Text = "重新连接服务器";
            button1.Enabled = true;
        }

        private bool notAllowClose = true;

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            SaveWeightData();
            e.Cancel = notAllowClose;
            base.OnFormClosing(e);

            if (initThread != null && initThread.IsAlive)
                initThread.Abort();
            if (fsm != null)
                fsm.Close(CloseForm);
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            label16.Text = $"{100 * trackBar1.Value / trackBar1.Maximum}";
            timer1.Stop();
            timer1.Start();
        }

        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            label17.Text = $"{100 * trackBar2.Value / trackBar2.Maximum}";
            timer1.Stop();
            timer1.Start();
        }

        private void trackBar3_Scroll(object sender, EventArgs e)
        {
            label19.Text = $"{100 * trackBar3.Value / trackBar3.Maximum}";
            timer1.Stop();
            timer1.Start();
        }

        private void trackBar6_Scroll(object sender, EventArgs e)
        {
            label18.Text = $"{100 * trackBar6.Value / trackBar6.Maximum}";
            timer1.Stop();
            timer1.Start();
        }

        private void trackBar5_Scroll(object sender, EventArgs e)
        {
            label21.Text = $"{100 * trackBar5.Value / trackBar5.Maximum}";
            timer1.Stop();
            timer1.Start();
        }

        private void trackBar4_Scroll(object sender, EventArgs e)
        {
            label20.Text = $"{100 * trackBar4.Value / trackBar4.Maximum}";
            timer1.Stop();
            timer1.Start();
        }

        private void trackBar7_Scroll(object sender, EventArgs e)
        {
            label2.Text = $"{100 * trackBar7.Value / trackBar7.Maximum}";
            timer1.Stop();
            timer1.Start();
        }

        private void trackBar8_Scroll(object sender, EventArgs e)
        {
            label7.Text = $"{100 * trackBar8.Value / trackBar8.Maximum}";
            timer1.Stop();
            timer1.Start();
        }

        private void trackBar9_Scroll(object sender, EventArgs e)
        {
            label9.Text = $"{100 * trackBar9.Value / trackBar9.Maximum}";
            timer1.Stop();
            timer1.Start();
        }
    }
}
